#include "TeamAI.h"
#include "Player.h"
#include "Team.h"
#include "ServiceProvider.h"
#include "AuctionManager.h"
#include "PlayerManager.h"
#include "PlayerFunctors.h"
#include "Utilities.h"
#include "RandomSystem.h"

#include <cassert>
#include <iostream>

namespace
{
	struct IsGoodChoice
	{
		IsGoodChoice (int i_role, int i_money)
			:m_role(i_role)
			,m_money(i_money)
		{
		}

		bool operator()(const Player* const i_player) const
		{
			return (i_player->GetBestRole() == m_role  && (m_money > i_player->BuyValue()));
		}

		int	m_role;
		int	m_money;
	};

	struct FormationPlayer
	{
		PlayerSkills skills;
		Player* player;
	};
	typedef std::vector<FormationPlayer> FormationPlayers;
	typedef std::vector<FormationPlayer*> FormationPlayersP;

	struct CompareByFormationSkill
	{
		explicit CompareByFormationSkill(const int i_skillNum)
			:m_skillNum(i_skillNum)
		{
		}
		bool operator()(const FormationPlayer* const i_first, const FormationPlayer* const i_second)
		{
			return i_first->skills.m_roleSkills[m_skillNum] > i_second->skills.m_roleSkills[m_skillNum];
		}
		int m_skillNum;
	};
}

TeamAI::TeamAI(ServiceProvider* const i_sp, Team* const i_team)
	:m_sp(i_sp)
	,m_team(i_team)
{
	assert(m_sp && m_team);
	m_map.resize(NUM_ROLES);
}

void TeamAI::TryBuy(const bool i_doNotPrompt)
{
	const PlayerVec& thePlayers = m_team->GetPlayersList();
	m_map[Goalkeeper].clear();
	m_map[Defender].clear();
	m_map[Attacker].clear();
	for(PlayerVecCit pit= thePlayers.begin(); pit!= thePlayers.end(); ++pit)
		m_map[(*pit)->GetBestRole()].push_back(*pit);

	const int teamBudget = m_team->GetBudget();
	if (teamBudget > 0)
	{
		AuctionManager& AM = m_sp->GetAuctionMgr();
		const PlayerVec& market = AM.GetMarket(); //it is sorted!

		static const TeamParams& params = Team::GetParams();
		static const int titolarPlayersPerRole[NUM_ROLES] = {
			params.TITOLARS_GOALKEEPERS,
			params.TITOLARS_DEFENDERS,
			params.TITOLARS_ATTACKERS};
		float ratioPlayersPerRole[NUM_ROLES];

		int i = 0;
		for (PlayerRoleMapCit it = m_map.begin(); it != m_map.end(); ++it, ++i)
		{
			ratioPlayersPerRole[i] = static_cast<float>(it->size()) / static_cast<float>(titolarPlayersPerRole[i]);
		
			if(ratioPlayersPerRole[i] < 1.6f)
			{
				const int playerBudget = static_cast<int>(teamBudget * RandFloatBetween(0.25f, 0.75f));
				PlayerVecCit found = std::find_if(market.begin(), market.end(), IsGoodChoice(i, playerBudget));
				if(found != market.end())
				{
					const size_t money = static_cast<int>((*found)->BuyValue() * RandFloatBetween(1.00f, 1.10f));
					AM.Bidding(AuctionManager::Bet(m_team, *found, money));
				}
			}
		}
	}
	TryCheat(i_doNotPrompt);
}

int TeamAI::TryCheat(const bool /*i_doNotPrompt*/)
{
	int num=0;
	while (m_team->GetPlayersList().size() + num < Team::GetParams().MIN_TEAM_PLAYERS)
	{
		size_t min = 99;
		int missingRole = Goalkeeper;
		int currRole = Goalkeeper;
		for (PlayerRoleMapCit it = m_map.begin(); it != m_map.end(); ++it, ++currRole)
		{
			const size_t i = it->size();
			if (i < min) 
			{
				min= i; 
				missingRole= currRole;
			}
		}

		Player* newPlayer = m_sp->GetPlayerMgr().CreateNewPlayer(false, true);
		newPlayer->SetRole(missingRole);
		newPlayer->SetTeam(m_team);
		m_map[missingRole].push_back(newPlayer);
		++num;
	}

	return num;
}

void TeamAI::TrySell(const bool i_doNotPrompt)
{
	static const float SALARY_FACTOR = 3.0f;
	static const TeamParams& params = Team::GetParams();
	static const size_t maximumSubstitutePlayersPerRole[NUM_ROLES] = 
		{params.TITOLARS_GOALKEEPERS*3, params.TITOLARS_DEFENDERS*3, params.TITOLARS_ATTACKERS*3};

	const PlayerVec& thePlayers = m_team->GetPlayersList();
	if(thePlayers.size() <= params.MIN_TEAM_PLAYERS)
		return;

	m_map[Goalkeeper].clear();
	m_map[Defender].clear();
	m_map[Attacker].clear();
	for(PlayerVecCit pit= thePlayers.begin(); pit!= thePlayers.end(); ++pit)
		m_map[(*pit)->GetBestRole()].push_back(*pit);

	Player* pToSell = 0;

	if(m_team->GetBudget() < 0) //desperate need money
	{
		pToSell = *(std::max_element(thePlayers.begin(), thePlayers.end(), PlayerComparisionSellValue));
	}
	else
	{
		int currRole = Goalkeeper;
		for(PlayerRoleMapIt it = m_map.begin(); it != m_map.end(); ++it, ++currRole)
		{
			if(it->size() > (maximumSubstitutePlayersPerRole[currRole])) // check if too much ppl in one role
			{
				if(m_team->GetBudget() > SALARY_FACTOR * m_team->CalculateTotalSalary() ) //we dont need money
					pToSell = it->back();
				else
					pToSell = it->at(it->size()/2); //we need money and we sell someone that we pay more.
			}
		}
	}

	if(pToSell && m_team->SellPlayer(pToSell))
		if(!i_doNotPrompt)
			std::cout << " ! " << m_team->GetName() << " ha venduto " << pToSell->GetName() << " per " << pToSell->SellValue() << " Banane!.\n";
}

void TeamAI::MakeFormation(const bool /*i_doNotPrompt*/)
{
	static TeamParams teamParams = Team::GetParams();
	static FormationPlayers formationPlayers(teamParams.MAX_TEAM_PLAYERS);
	static FormationPlayersP formationPlayersP(teamParams.MAX_TEAM_PLAYERS);

	PlayerVec thePlayers = m_team->GetPlayersList();
	formationPlayersP.resize(thePlayers.size());
	for(size_t i=0; i< formationPlayersP.size(); ++i)
	{
		FormationPlayer& theFPlayer = formationPlayers[i];
		formationPlayersP[i] = &theFPlayer;
		theFPlayer.player = thePlayers[i];
		const Player& thePlayer = *theFPlayer.player;

		const PlayerSkills& skills = thePlayer.GetSkills();
		const int brokenFactor = (thePlayer.IsBroken() ? -1000 : 0);
		const int resFactor = skills.m_resistance/10;
		const int fatFactor = - skills.m_fatigue;
		const int factors = brokenFactor + resFactor + fatFactor;
		theFPlayer.skills.m_roleSkills[0] = skills.m_roleSkills[0] + factors;
		theFPlayer.skills.m_roleSkills[1] = skills.m_roleSkills[1] + factors;
		theFPlayer.skills.m_roleSkills[2] = skills.m_roleSkills[2] + factors;
		theFPlayer.skills.m_bestRole = skills.m_bestRole;
	}
	typedef FormationPlayersP::iterator FPIt;
	FPIt startGK = formationPlayersP.begin();
	FPIt startDef = startGK + teamParams.TITOLARS_GOALKEEPERS;
	FPIt startAtt = startDef + teamParams.TITOLARS_DEFENDERS;
	FPIt startNoobs = startAtt + teamParams.TITOLARS_ATTACKERS;
	FPIt end = formationPlayersP.end();
	std::nth_element(startGK, startDef, end, CompareByFormationSkill(Goalkeeper));
	std::nth_element(startDef, startAtt, end, CompareByFormationSkill(Defender));
	std::nth_element(startAtt, startNoobs, end, CompareByFormationSkill(Attacker));

	for(size_t i=0; i< formationPlayersP.size(); ++i)
		thePlayers[i] = formationPlayersP[i]->player;
	m_team->SetPlayersList(thePlayers);
}
